﻿#define _CRT_SECURE_NO_WARNINGS

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <map>

#include <windows.h>

#include "server.pb.h"

// 获取可执行程序的真实路径
const char *get_realpath()
{
	char buff[MAX_PATH + 1] = { 0 };
	DWORD size = GetModuleFileNameA(NULL, buff, MAX_PATH);
	if (size == 0) return nullptr;
	for (DWORD i = size; i > 0; --i) {
		if (buff[i - 1] == '/' || buff[i - 1] == '\\') break;
		buff[i - 1] = '\0';
	}
	return buff;
}

enum ServerType
{
	HTTP = 0,
	MAIL = 1
};

struct Session
{
	std::string uuid;
	uint64_t userid;
};

struct UserInfo
{
	std::string key;
	std::string name;
};

struct Server
{
	int32_t iflag;
	bool running;
	std::vector<int32_t> address;
	std::string hostname;
	ServerType type;
	struct InternalSession {
		std::string key;
		Session value;
	};
	std::vector<InternalSession> internal;
	std::map<std::string, Session> sessions;
	std::vector<UserInfo> users;
};

using google::protobuf::Message;
using google::protobuf::Descriptor;
using google::protobuf::FieldDescriptor;

// conflict with window message
#ifdef GetMessage
#undef GetMessage
#endif

static bool StructToProtoInternal(const uint8_t *&bytes, Message &msg);
static bool ProtoToStructInternal(uint8_t *&bytes, const Message &msg);

template<typename Ty>
inline int GetFieldSize(const FieldDescriptor &field)
{
	return field.is_repeated() ? sizeof(std::vector<Ty>) : sizeof(Ty);
}

// 获取message所对应的结构体的size
static int GetMessageStructSize(const Descriptor *desc)
{
	static_assert(
		sizeof(std::vector<char>) == sizeof(std::vector<char[8]>),
		"sizeof std::vector is variable, can not use this function.");
	static_assert(
		sizeof(std::map<char, char>) == sizeof(std::map<char[8], char[8]>),
		"sizeof std::map is variable, can not use this function.");
	int size = 0;
	int count = desc->field_count();
	for (int i = 0; i < count; ++i) {
		auto field = desc->field(i);
		switch (field->cpp_type()) {
		case FieldDescriptor::CPPTYPE_INT32:
		case FieldDescriptor::CPPTYPE_UINT32:
		case FieldDescriptor::CPPTYPE_FLOAT:
			size += GetFieldSize<int32_t>(*field);
			break;
		case FieldDescriptor::CPPTYPE_INT64:
		case FieldDescriptor::CPPTYPE_UINT64:
		case FieldDescriptor::CPPTYPE_DOUBLE:
			size += GetFieldSize<int64_t>(*field);
			break;
		case FieldDescriptor::CPPTYPE_BOOL:
			size += GetFieldSize<bool>(*field);
			break;
		case FieldDescriptor::CPPTYPE_ENUM:
			size += GetFieldSize<int>(*field);
			break;
		case FieldDescriptor::CPPTYPE_STRING:
			size += GetFieldSize<std::string>(*field);
			break;
		case FieldDescriptor::CPPTYPE_MESSAGE:
		{
			if (field->is_map()) {
				size += sizeof(std::map<int, int>);
			} else if (field->is_repeated()) {
				size += sizeof(std::vector<int>);
			} else {
				size += GetMessageStructSize(field->message_type());
			}
			break;
		}
		default:
			return 0;
		}
	}
	return size;
}

#define SetProtoValue(type, Type) \
do { if (field->is_repeated()) { \
	auto &values = *(const std::vector<type>*)bytes; \
	for (const type &value : values) { \
		refl->Add ## Type(&msg, field, value); \
	} \
	bytes += sizeof(std::vector<type>); \
} else { \
	refl->Set ## Type(&msg, field, *(const type*)bytes); \
	bytes += sizeof(type); \
}} while(0)

static bool StructToProtoInternal(const uint8_t *&bytes, Message &msg)
{
	auto refl = msg.GetReflection();
	auto desc = msg.GetDescriptor();
	for (int i = 0; i < desc->field_count(); ++i) {
		auto field = desc->field(i);
		switch (field->cpp_type()) {
		case FieldDescriptor::CPPTYPE_INT32:
			SetProtoValue(int32_t, Int32);
			break;
		case FieldDescriptor::CPPTYPE_INT64:
			SetProtoValue(int64_t, Int64);
			break;
		case FieldDescriptor::CPPTYPE_UINT32:
			SetProtoValue(uint32_t, UInt32);
			break;
		case FieldDescriptor::CPPTYPE_UINT64:
			SetProtoValue(uint64_t, UInt64);
			break;
		case FieldDescriptor::CPPTYPE_DOUBLE:
			SetProtoValue(double, Double);
			break;
		case FieldDescriptor::CPPTYPE_FLOAT:
			SetProtoValue(float, Float);
			break;
		case FieldDescriptor::CPPTYPE_BOOL:
			SetProtoValue(bool, Bool);
			break;
		case FieldDescriptor::CPPTYPE_ENUM:
			SetProtoValue(int, EnumValue);
			break;
		case FieldDescriptor::CPPTYPE_STRING:
			SetProtoValue(std::string, String);
			break;
		case FieldDescriptor::CPPTYPE_MESSAGE:
		{
			int size = GetMessageStructSize(field->message_type());
			if (field->is_map()) {
				auto &values = *(std::map<std::string, uint8_t>*)bytes;
				for (auto &pair : values) {
					auto submsg = refl->AddMessage(&msg, field);
					auto data = (const uint8_t *)&pair;
					if (!StructToProtoInternal(data, *submsg)) return false;
				}
				bytes += sizeof(std::map<std::string, uint8_t>);
			} else if (field->is_repeated()) {
				auto &values = *(std::vector<uint8_t>*)bytes;
				const uint8_t *data = values.data();
				if (values.size() % size) return false;
				int count = (int)values.size() / size;
				for (int i = 0; i < count; ++i) {
					auto submsg = refl->AddMessage(&msg, field);
					if (!StructToProtoInternal(data, *submsg)) return false;
				}
				bytes += sizeof(std::vector<uint8_t>);
			} else {
				auto submsg = refl->MutableMessage(&msg, field);
				if (!StructToProtoInternal(bytes, *submsg)) return false;
			}
			break;
		}
		default:
			return false; // never reach;
		}
	}
	return true;
}

#define SetStructValue(type, Type) \
do { if (field->is_repeated()) { \
	auto &values = *(std::vector<type>*)bytes; \
	int count = refl->FieldSize(msg, field); \
	for (int i = 0; i < count; ++i) { \
		values.push_back(refl->GetRepeated ## Type(msg, field, i)); \
	} \
	bytes += sizeof(std::vector<type>); \
} else { \
	*(type*)bytes = refl->Get ## Type(msg, field); \
	bytes += sizeof(type); \
}} while(0)

template<int size>
bool SetStructMap(uint8_t *bytes, const Message &msg, const FieldDescriptor *field)
{
	auto refl = msg.GetReflection();
	struct holder { uint8_t member[size]; };
	auto &map = *(std::map<std::string, holder>*)bytes;
	std::pair<std::string, holder> pair;
	int count = refl->FieldSize(msg, field);
	for (int i = 0; i < count; ++i) {
		auto &submsg = refl->GetRepeatedMessage(msg, field, i);
		auto data = (uint8_t*)&pair;
		if (!ProtoToStructInternal(data, submsg)) return false;
		map[pair.first] = pair.second;
	}
	return true;
}

static bool ProtoToStructInternal(uint8_t *&bytes, const Message &msg)
{
	auto refl = msg.GetReflection();
	auto desc = msg.GetDescriptor();
	for (int i = 0; i < desc->field_count(); ++i) {
		auto field = desc->field(i);
		switch (field->cpp_type()) {
		case FieldDescriptor::CPPTYPE_INT32:
			SetStructValue(int32_t, Int32);
			break;
		case FieldDescriptor::CPPTYPE_INT64:
			SetStructValue(int64_t, Int64);
			break;
		case FieldDescriptor::CPPTYPE_UINT32:
			SetStructValue(uint32_t, UInt32);
			break;
		case FieldDescriptor::CPPTYPE_UINT64:
			SetStructValue(uint64_t, UInt64);
			break;
		case FieldDescriptor::CPPTYPE_DOUBLE:
			SetStructValue(double, Double);
			break;
		case FieldDescriptor::CPPTYPE_FLOAT:
			SetStructValue(float, Float);
			break;
		case FieldDescriptor::CPPTYPE_BOOL:
			SetStructValue(bool, Bool);
			break;
		case FieldDescriptor::CPPTYPE_ENUM:
			SetStructValue(int, EnumValue);
			break;
		case FieldDescriptor::CPPTYPE_STRING:
			SetStructValue(std::string, String);
			break;
		case FieldDescriptor::CPPTYPE_MESSAGE:
		{
			int size = GetMessageStructSize(field->message_type());
			if (field->is_map()) {
				if (size <= 8) {
					if (!SetStructMap<8>(bytes, msg, field)) return false;
				} else if (size <= 16) {
					if (!SetStructMap<16>(bytes, msg, field)) return false;
				} else if (size <= 32) {
					if (!SetStructMap<32>(bytes, msg, field)) return false;
				} else if (size <= 64) {
					if (!SetStructMap<64>(bytes, msg, field)) return false;
				} else if (size <= 128) {
					if (!SetStructMap<128>(bytes, msg, field)) return false;
				} else if (size <= 256) {
					if (!SetStructMap<256>(bytes, msg, field)) return false;
				} else if (size <= 512) {
					if (!SetStructMap<512>(bytes, msg, field)) return false;
				} else if (size <= 1024) {
					if (!SetStructMap<1024>(bytes, msg, field)) return false;
				} else if (size <= 2048) {
					if (!SetStructMap<2048>(bytes, msg, field)) return false;
				} else if (size <= 4096) {
					if (!SetStructMap<4096>(bytes, msg, field)) return false;
				} else if (size <= 8192) {
					if (!SetStructMap<8192>(bytes, msg, field)) return false;
				} else {
					throw google::protobuf::FatalException(__FILE__, __LINE__,
						"The struct is too big, see define of field.");
					return false;
				}
				bytes += sizeof(std::map<std::string, uint8_t>);
			} else if (field->is_repeated()) {
				auto &values = *(std::vector<uint8_t>*)bytes;
				int count = refl->FieldSize(msg, field);
				values.resize(count * size);
				auto data = (uint8_t*)values.data();
				for (int i = 0; i < count; ++i) {
					auto &submsg = refl->GetRepeatedMessage(msg, field, i);
					if (!ProtoToStructInternal(data, submsg)) return false;
				}
				bytes += sizeof(std::vector<uint8_t>);
			} else {
				auto &submsg = refl->GetMessage(msg, field);
				if (!ProtoToStructInternal(bytes, submsg)) return false;
			}
			break;
		}
		default:
			break;
		}
	}
	return true;
}

template<typename STRUCT, typename PROTO>
bool StructToProto(const STRUCT &in, PROTO &out)
{
	int size = GetMessageStructSize(out.GetDescriptor());
	if (size != sizeof(STRUCT)) return false;
	auto bytes = (const uint8_t*)&in;
	return StructToProtoInternal(bytes, out);
}

template<typename PROTO, typename STRUCT>
bool ProtoToStruct(const PROTO &in, STRUCT &out)
{
	int size = GetMessageStructSize(in.GetDescriptor());
	if (size != sizeof(STRUCT)) return false;
	auto bytes = (uint8_t*)&out;
	return ProtoToStructInternal(bytes, in);
}

int main(int argc, char *argv[])
{
	Server server;
	server.iflag = 99;
	server.running = true;
	server.address.push_back(12345678);
	server.address.push_back(87654321);
	server.type = ServerType::MAIL;
	server.internal.push_back(Server::InternalSession{ "asjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "bsjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "csjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "dsjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "isjdifasdflkjasdkfa", Session{ "erererrerqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "dsjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "jsjdifasdflkjasdkfa", Session{ "erereererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "fasdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdifasdflkjasdkfa", Session{ "ererersererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjDIFASDFLKJASDKFA", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjijjikfslkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdifasFJKJjasdkfa", Session{ "erererererweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdifa32323jasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdifasdfl89809kfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdiflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdijasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdidkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdsdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.internal.push_back(Server::InternalSession{ "asjdifasdflkjasdkfa", Session{ "erererererqweqwe", 239283 } });
	server.sessions.emplace("ff", Session{ "gg", 123 });
	server.sessions.emplace("kk", Session{ "aa", 345 });
	server.users.emplace_back(UserInfo{ "ijij", "jiok" });
	server.users.emplace_back(UserInfo{ "ao0f", "8558" });
	server.users.emplace_back(UserInfo{ "fsajflkdsjaflksdjflkasdfjlkasdjflkasdjflaksdfao0f", "8fasdfjalksdfjlaksdjfkalsdjflaksjkdfajsldf558" });
	server.users.emplace_back(UserInfo{ "fsajflkcvnakjsdhfaehfoifjlkasdjflkasdjflaksdfao0f", "8vndkfahefhaejlaksdjfkalsdjflaksjkdfajsldf558" });

	com::woody::Server proto;
	if (StructToProto(server, proto)) {
		printf("success, %s\n", proto.sessions().at("ff").uuid().c_str());
	} else {
		printf("failed\n");
	}
	Server server2;
	if (ProtoToStruct(proto, server2)) {
		printf("success, %s\n", proto.sessions().at("ff").uuid().c_str());
	} else {
		printf("failed\n");
	}
	return 0;
}

